package cn.yujiago.springboot.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import cn.yujiago.springboot.exception.CustomModularRealmAuthenticator;
import cn.yujiago.springboot.realm.PhoneRealm;
import cn.yujiago.springboot.realm.UserRealm;
import org.apache.shiro.authc.AbstractAuthenticator;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

@Configuration
public class ShiroConfig {

    /**
     * 缓存管理器
     */
    @Bean(name = "shiroCacheManager")
    public CacheManager shiroCacheManager(){
//        EhCacheManager em = new EhCacheManager();
//        em.setCacheManagerConfigFile("classpath:config/ehcache.xml");
//        return em;
        return new MemoryConstrainedCacheManager();
    }

    /**
     * 加密策略
     */
    @Bean
    public CredentialsMatcher credentialsMatcher(){
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        // 加密算法：MD5、SHA1
        credentialsMatcher.setHashAlgorithmName(Constants.Hash_Algorithm_Name);
        // 散列次数
        credentialsMatcher.setHashIterations(Constants.Hash_Iterations);
        return credentialsMatcher;
    }

    /**
     * 自定义Realm
     */
    @Bean
    public UserRealm userRealm(CredentialsMatcher credentialsMatcher) {
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(credentialsMatcher);
        userRealm.setCacheManager(shiroCacheManager());
        return userRealm;
    }
    @Bean
    public PhoneRealm phoneRealm(){
        PhoneRealm phoneRealm = new PhoneRealm();
        phoneRealm.setCacheManager(shiroCacheManager());
        return phoneRealm;
    }

    /**
     * 认证器
     */
    @Bean
    public AbstractAuthenticator abstractAuthenticator(UserRealm userRealm, PhoneRealm phoneRealm){
        ModularRealmAuthenticator authenticator = new CustomModularRealmAuthenticator();
        // 认证策略：AtLeastOneSuccessfulStrategy(默认)，AllSuccessfulStrategy，FirstSuccessfulStrategy
        authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        // 加入realms
        List<Realm> realms = new ArrayList<>();
        realms.add(userRealm);
        realms.add(phoneRealm);
        authenticator.setRealms(realms);
        return authenticator;
    }

    @Bean
    SessionDAO sessionDAO() {
        return new MemorySessionDAO();
    }

    @Bean
    public SessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // 自定义监听器
//        Collection<SessionListener> listeners = new ArrayList<SessionListener>();
//        listeners.add(new BDSessionListener());
//        sessionManager.setSessionListeners(listeners);
        sessionManager.setSessionDAO(sessionDAO());
        return sessionManager;
    }

    @Bean
    public SecurityManager securityManager(UserRealm userRealm, PhoneRealm phoneRealm, AbstractAuthenticator abstractAuthenticator) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm
        List<Realm> realms = new ArrayList<>();
        realms.add(userRealm);
        realms.add(phoneRealm);
        securityManager.setRealms(realms);
        // 自定义缓存实现，可以使用redis
        securityManager.setCacheManager(shiroCacheManager());
        // 自定义session管理，可以使用redis
        securityManager.setSessionManager(sessionManager());
        // 注入记住我管理器
        securityManager.setRememberMeManager(rememberMeManager());
        // 认证器
        securityManager.setAuthenticator(abstractAuthenticator);
        return securityManager;
    }

    @Bean
    ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 登录页面，认证失败时所跳转的页面
        shiroFilterFactoryBean.setLoginUrl("/page/anon/login");
        // 认证成功时所跳转的页面
        shiroFilterFactoryBean.setSuccessUrl("/page/common/index");
        // 访问未授权页面或功能时所跳转的页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/page/anon/403");
        //配置访问权限控制规则
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 静态资源
        filterChainDefinitionMap.put("/static/**", "anon");

        filterChainDefinitionMap.put("/", "anon");
        filterChainDefinitionMap.put("/druid/**", "anon");
        filterChainDefinitionMap.put("/page/anon/**", "anon");
        filterChainDefinitionMap.put("/user/dologin", "anon");
        filterChainDefinitionMap.put("/user/plogin", "anon");
        filterChainDefinitionMap.put("/user/pRegister", "anon");
        filterChainDefinitionMap.put("/user/checkPhone", "anon");
        filterChainDefinitionMap.put("/user/checkUsername", "anon");
        filterChainDefinitionMap.put("/user/sendMessageCode", "anon");
        filterChainDefinitionMap.put("/image/list", "anon");
//        filterChainDefinitionMap.put("/user/logout", "logout");
        filterChainDefinitionMap.put("/page/perm/**", "roles[admin]");
        filterChainDefinitionMap.put("/page/content/**", "roles[user]");
        filterChainDefinitionMap.put("/**", "authc"); //表示需要认证才可以访问
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            @Qualifier("securityManager") SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * cookie对象
     */
    public SimpleCookie rememberMeCookie(){
        //这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //<!-- 记住我cookie生效时间30天 ,单位秒 -->
        simpleCookie.setMaxAge(2592000);
        return simpleCookie;
    }
    /**
     * cookie管理对象，记住我功能
     */
    public CookieRememberMeManager rememberMeManager(){
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));
        return cookieRememberMeManager;
    }
    /**
     * 支持shiro标签
     * @return
     */
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}
